跳到主要内容

SSH 远程登陆基本使用

SSH 服务远程连接

SSH 是什么

简单说,SSH (Secure Shell)是一种网络协议,用于计算机之间的加密登录。

如果一个用户从本地计算机,使用SSH协议登录另一台远程计算机,我们就可以认为,这种登录是安全的,即使被中途截获,密码也不会泄露。

最早的时候,互联网通信都是明文通信,一旦被截获,内容就暴露无疑。1995年,芬兰学者Tatu Ylonen设计了 SSH 协议,将登录信息全部加密,成为互联网安全的一个基本解决方案,迅速在全世界获得推广,目前已经成为 Linux系统的标准配置。

需要指出的是,SSH只是 一种协议,存在多种实现,既有商业实现,也有开源实现。本文针对的实现是 OpenSSH,它是自由软件,应用非常广泛,是所有操作系统的默认组件。

原理介绍

SSH 之所以一经提出,就得到了快速发展,是因为数据的安全性对任何人都非常重要。这里我们对其保护数据安全的原理进行探究。

先介绍一下几个密码学的基本概念:

  • 明文 plaintext 指传送方(一般指客户端)想要接受方(一般指服务端)获得的可读信息
  • 密文 ciphertext 指明文进过加密后所产生的信息
  • 秘钥 key 指用来完成加密、解密、完整性验证等密码学应用的密码信息,是明文转换为密文或密文转换为明文的算法需要的参数
  • 私钥 指私有的秘钥
  • 公钥 指公开的秘钥

允许远程登陆 root

编辑 /etc/ssh/sshd_config文件;

sudo vim /etc/ssh/sshd_config

找到配置参数:PermitRootLogin

将该参数后面的值修改为 yes 即可;

重启 ssh 服务

sudo  systemctl  restart  ssh
sudo systemctl restart sshd

生成公钥密钥

$ ssh-keygen -t rsa -b 4096

上面的命令会在 ~/.ssh 目录生成一对密钥:id_rsa(私钥)和 id_rsa.pub(公钥)。

这个命令的各个参数含义如下。

  • -t rsa:指定密钥算法 RSA。
  • -b 4096:指定密钥的位数是4096位。安全性要求不高的场合,这个值可以小一点,但是不应小于1024。
  • -f ~/.ssh/id_rsa:指定生成密钥的位置和文件名。
  • -C id_rsa:指定密钥的识别字符串,相当于注释,可以随意设置。

安装 SSH 客户端

OpenSSH 的客户端是二进制程序 ssh。它在 Linux/Unix 系统的位置是 /usr/local/bin/ssh,Windows 系统的位置是 \Program Files\OpenSSH\bin\ssh.exe

Linux 系统一般都自带 ssh,如果没有就需要安装。

$ sudo apt install openssh-client

安装以后,可以使用 -V 参数输出版本号,查看一下是否安装成功。

ssh -V

image.png

基本用法

ssh 最常见的用途就是登录服务器,这要求服务器安装并正在运行 SSH 服务器软件。

ssh 登录服务器的命令如下

$ ssh hostname

上面命令中,hostname是主机名,它可以是域名,也可能是 IP 地址或局域网内部的主机名。不指定用户名的情况下,将使用客户端的当前用户名,作为远程服务器的登录用户名。如果要指定用户名,可以采用下面的语法。

$ ssh user@hostname

上面的命令中,用户名和主机名写在一起,之间使用 @ 分隔。

用户名也可以使用 ssh 的 -l 参数指定,这样的话,用户名和主机名就不用写在一起了。

$ ssh -l username host

ssh 默认连接服务器的 22 端口,-p 参数可以指定其他端口

$ ssh -p 8821 foo.com

上面命令连接服务器 foo.com 的 8821 端口。

SSH 密钥登录

密钥登录的过程

SSH 密钥登录分为以下的步骤

预备步骤,客户端通过ssh-keygen生成自己的公钥和私钥。

第一步,手动将客户端的公钥放入远程服务器的指定位置。

第二步,客户端向服务器发起 SSH 登录的请求。

第三步,服务器收到用户 SSH 登录的请求,发送一些随机数据给用户,要求用户证明自己的身份。

第四步,客户端收到服务器发来的数据,使用私钥对数据进行签名,然后再发还给服务器。

第五步,服务器收到客户端发来的加密签名后,使用对应的公钥解密,然后跟原始数据比较。如果一致,就允许用户登录。

手动上传公钥

OpenSSH 规定,用户公钥保存在服务器的 ~/.ssh/authorized_keys 文件。你要以哪个用户的身份登录到服务器,密钥就必须保存在该用户主目录的 ~/.ssh/authorized_keys 文件。

只要把公钥添加到这个文件之中,就相当于公钥上传到服务器了。每个公钥占据一行。如果该文件不存在,可以手动创建。

用户可以手动编辑该文件,把公钥粘贴进去,也可以在本机计算机上,执行下面的命令。

$ cat ~/.ssh/id_rsa.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# 然后会要求你输入远程服务的密码

注意,authorized_keys 文件的权限要设为 644,即只有文件所有者才能写。如果权限设置不对,SSH 服务器可能会拒绝读取该文件。

$ chmod 644 ~/.ssh/authorized_keys

只要公钥上传到服务器,下次登录时,OpenSSH 就会自动采用密钥登录,不再提示输入密码。

ssh-copy-id 命令:自动上传公钥

注意:PowerShell 没有这个

OpenSSH 自带一个 ssh-copy-id 命令,可以自动将公钥拷贝到远程服务器的 ~/.ssh/authorized_keys 文件。如果 ~/.ssh/authorized_keys 文件不存在,ssh-copy-id 命令会自动创建该文件。

用户在本地计算机执行下面的命令,就可以把本地的公钥拷贝到服务器。

$ ssh-copy-id -i key_file user@host

上面命令中,-i 参数用来指定公钥文件,user 是所要登录的账户名,host 是服务器地址。如果省略用户名,默认为当前的本机用户名。执行完该命令,公钥就会拷贝到服务器。

注意,公钥文件可以不指定路径和 .pub 后缀名,ssh-copy-id 会自动在 ~/.ssh 目录里面寻找。

$ ssh-copy-id -i id_rsa user@host

上面命令中,公钥文件会自动匹配到 ~/.ssh/id_rsa.pub

ssh-copy-id 会采用密码登录,系统会提示输入远程服务器的密码。

注意,ssh-copy-id 是直接将公钥添加到 authorized_keys 文件的末尾。如果 authorized_keys 文件的末尾不是一个换行符,会导致新的公钥添加到前一个公钥的末尾,两个公钥连在一起,使得它们都无法生效。所以,如果 authorized_keys 文件已经存在,使用 ssh-copy-id 命令之前,务必保证 authorized_keys 文件的末尾是换行符(假设该文件已经存在)。

连接流程

ssh 连接远程服务器后,首先有一个验证过程,验证远程服务器是否为陌生地址。

如果是第一次连接某一台服务器,命令行会显示一段文字,表示不认识这台机器,提醒用户确认是否需要连接。

The authenticity of host 'foo.com (192.168.121.111)' can't be established.
ECDSA key fingerprint is SHA256:Vybt22mVXuNuB5unE++yowF7lgA/9/2bLSiO3qmYWBY.
Are you sure you want to continue connecting (yes/no)?

上面这段文字告诉用户,foo.com 这台服务器的指纹是陌生的,让用户选择是否要继续连接(输入 yes 或 no)。

所谓“服务器指纹”,指的是 SSH 服务器公钥的哈希值。每台 SSH 服务器都有唯一一对密钥,用于跟客户端通信,其中公钥的哈希值就可以用来识别服务器。

下面的命令可以查看某个公钥的指纹。

$ ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub
256 SHA256:O7TGZ/1vMts+3lbDeTJEJtAyYLwJ43SmasNF4slKmoM root@iZwz9iz5k8lw86i5kl33hjZ (ECDSA)

ssh 会将本机连接过的所有服务器公钥的指纹,都储存在本机的 ~/.ssh/known_hosts 文件中。每次连接服务器时,通过该文件判断是否为陌生主机(陌生公钥)。

在上面这段文字后面,输入 yes,就可以将当前服务器的指纹也储存在本机 ~/.ssh/known_hosts 文件中,并显示下面的提示。以后再连接的时候,就不会再出现警告了。

服务器密钥变更

服务器指纹可以防止有人恶意冒充远程主机。如果服务器的密钥发生变更(比如重装了 SSH 服务器),客户端再次连接时,就会发生公钥指纹不吻合的情况。这时,客户端就会中断连接,并显示一段警告信息。

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
77:a5:69:81:9b:eb:40:76:7b:13:04:a9:6c:f4:9c:5d.
Please contact your system administrator.
Add correct host key in /home/me/.ssh/known_hosts to get rid of this message.
Offending key in /home/me/.ssh/known_hosts:36

上面这段文字的意思是,该主机的公钥指纹跟 ~/.ssh/known_hosts 文件储存的不一样,必须处理以后才能连接。这时,你需要确认是什么原因,使得公钥指纹发生变更,到底是恶意劫持,还是管理员变更了 SSH 服务器公钥。

如果新的公钥确认可以信任,需要继续执行连接,可以执行下面的命令,将原来的公钥指纹从 ~/.ssh/known_hosts 文件删除。

$ ssh-keygen -R hostname

上面命令中,hostname 是发生公钥变更的主机名。

执行远程命令

SSH 登录成功后,用户就进入了远程主机的命令行环境,所看到的提示符,就是远程主机的提示符。这时,你就可以输入想要在远程主机执行的命令。

另一种执行远程命令的方法,是将命令直接写在 ssh 命令的后面。

$ ssh username@hostname command

上面的命令会使得 SSH 在登录成功后,立刻在远程主机上执行命令 command。

如下所示

$ ssh foo@server.example.com cat /etc/hosts

上面的命令会在登录成功后,立即远程执行命令 cat /etc/hosts

采用这种语法执行命令时,ssh 客户端不会提供互动式的 Shell 环境,而是直接远程命令的执行结果输出在命令行。

但是,有些命令需要互动式的 Shell 环境,这时就要使用 -t 参数

# 报错
$ ssh remote.server.com emacs
emacs: standard input is not a tty

# 不报错
$ ssh -t server.example.com emacs

上面代码中,emacs命令需要一个互动式 Shell,所以报错。只有加上 -t 参数,ssh 才会分配一个互动式 Shell。

ssh 命令行配置项

ssh 命令有很多配置项,修改它的默认行为。

1、-c 参数指定加密算法。

$ ssh -c blowfish,3des server.example.com
# 或者
$ ssh -c blowfish -c 3des server.example.com

上面命令指定使用加密算法 blowfish 或 3des。

2、-C 参数表示压缩数据传输。

$ ssh -C server.example.com

3、-D 参数指定本机的 Socks 监听端口,该端口收到的请求,都将转发到远程的 SSH 主机,又称动态端口转发

$ ssh -D 1080 server

上面命令将本机 1080 端口收到的请求,都转发到服务器 server。

4、-f 参数表示 SSH 连接在后台运行。

5、-F 参数指定配置文件。

$ ssh -F /usr/local/ssh/other_config

上面命令指定使用配置文件 other_config

6、-i 参数用于指定私钥,意为 identity_file,默认值为 ~/.ssh/id_dsa

注意,对应的公钥必须存放到服务器

$ ssh -i my-key server.example.com

7、-l 参数指定远程登录的账户名。

$ ssh -l sally server.example.com
# 等同于
$ ssh sally@server.example.com

8、-L 参数设置本地端口转发

$ ssh  -L 9999:targetServer:80 user@remoteserver

上面命令中,所有发向本地 9999 端口的请求,都会经过 remoteserver 发往 targetServer 的 80 端口,这就相当于直接连上了 targetServer 的 80 端口。

9、-N 参数用于端口转发,表示建立的 SSH 只用于端口转发,不能执行远程命令,这样可以提供安全性

Reference

参考资料 SSH 基本知识 参考资料 SSH原理与运用 参考资料 深入了解SSH